GDB Test Drive
Writeupt6o_o6t.icon
問題文に書いてあることをそのまま行う
正しく動作する:
code:pseudo.c
char l[] = {0x41, 0x3a, 0x34, 0x40, 0x72, 0x25, 0x75, 0x4c, 0x35, 0x62, 0x33, 0x46, 0x38, 0x38, 0x62, 0x43, 0x30, 0x35, 0x43, 0x60, 0x47, 0x62, 0x30, 0x66, 0x66, 0x66, 0x65, 0x35, 0x66, 0x64, 0x67, 0x4e};
char *rotate_encrypt(int _, char *input);
int main(void)
{
char *ans;
// sleep(100000);
ans = rotate_encrypt(0, l);
fputs(ans, stdout);
putchar('\n');
free(ans);
return 0;
}
char *rotate_encrypt(int _, char *input)
{
// t: rbp-0x30
// v: rbp-0x1c
// i: rbp-0x18
// s: rbp-0x10
// len: rbp-0x8
int i, v;
size_t len;
char *t, *s;
t = input;
s = strdup(t);
len = strlen(s);
i = 0;
while (i < len)
{
if (si > 0x20 && si != 0x7f) {
if (v > 0x7e)
{
// rotate_encrypt+127
}
else
{
// rotate_encrypt+150
}
}
i++;
}
return s;
}
間違っている(最初に書いたもの):
code:pseudo_old.c
int main(void) {
char a = 0; // a: rbp-0x10
int b = 0x6666, c = 0x3530, d = 0x6235;
char *s = 0x4c75257240343a41, *ans; // s: rbp-0x30
sleep(100000);
ans = rotete_encrypt(0, s);
fputs(ans, stdout);
putchar('\n');
free(ans);
return 0;
}
char* rotate_encrypt(int16_t x, unsigned char* input) {
int p = x, i, v; // v: rbp-0x1c
size_t len; // len: rbp-0x8
char* t, s;
s = strdup(input); // s: rbp-0x10
len = strlen(s);
i = 0; // i: rbp-0x18
while (len > i) {
if ((int)si < 32 && (int)si != 0x7f) { if (v < 0x7e) {
// rotate_encrypt+127
} else {
// rotate_encrypt+150
}
}
i++;
}
return u;
}
main+34 ~ main+58
main+58の時点で、rbp-0x30からの2バイトは0x41, 0x3a
実際はリトルエンディアンだから、0x3a41
rbp-0x28からの2バイトは0x35, 0x62
0x6235
rbp-0x20
0x3530
rbp-0x18
0x6666
rbp-0x10
0x00
sleepについて
manでは以下のようにあるから、一応100000秒 = 1日と約3時間放置すればフラグが表示される計算 Pause for NUMBER seconds.
rotate_encryptを読む段階へ
最初に、strdupを呼び出しているのが視える
引数rdiは、rbp-0x30、元々第二引数が保存されている場所の値
つまり、第二引数はconst char *であり、そのポインタがstrdupに渡されている
rotate_encrypt+62~178は、ループになっている
jb命令の読み方が分からない
rbp-0x18とrbp-0x8を比較して、どちらが大きかったらjmpするのか?
rotate_encrypt+70について
Adds the destination operand (first operand) and the source operand (second operand) and then stores the result in the destination operand.
したがって、add rax, rdxでは、文字列uのポインタ値に、qの値を足したものがraxにセットされる
一文字ずつ見ているということ
https://scrapbox.io/files/6517f1f1d768880021b52e46.png
movzx eax, BYTE PTR [rax]
raxの下位1バイト(=al)をeaxにゼロ拡張(ゼロ埋め)
cmp al, 0x20
下位1バイトを0x20と比較
rotate_encrypt+127からのecxへの処理は無視してよいのでは?
ecxに入った値の下位1バイトがedxを経由してraxに入るが、jmp直後の+174でraxは書き換えられる
ecxはこの部分以外では使われていない
rdxは、ループが継続する場合には+62ですぐに書き換わる
と思ったが、raxに入るのではなく、raxのアドレス = u[q]に入ることに気づいた
つまり、ecxの計算結果の下位1バイトがu[q]に入る
計算をしたいから?
raxの値は
info registersで見てもecxには有効なアドレス値ではなく、0x43のような値が入っていた
leaはアドレス計算のための命令ではないの?
なぜアドレスを計算せず、普通の値の計算をしている?
命令を少なくできるというメリットはあるのかもしれない
code:s.txt
(gdb) x/16 $rbp-0x30
0x7fffffffdef0: 0x41 0x3a 0x34 0x40 0x72 0x25 0x75 0x4c
0x7fffffffdef8: 0xff 0xfb 0x8b 0x1f 0x00 0x00 0x00 0x00
この結果から、sが指している文字列は、..
code:s.txt
(gdb) x/42z $rax
0x7fffffffdef0: 0x41 0x3a 0x34 0x40 0x72 0x25 0x75 0x4c
0x7fffffffdef8: 0x35 0x62 0x33 0x46 0x38 0x38 0x62 0x43
0x7fffffffdf00: 0x30 0x35 0x43 0x60 0x47 0x62 0x30 0x66
0x7fffffffdf08: 0x66 0x66 0x65 0x35 0x66 0x64 0x67 0x4e
0x7fffffffdf10: 0x00 0x10 0x00 0x00 0x00 0x00 0x00 0x00
あれ?t6o_o6t.icon
左側のアドレスの表示から考えると、0x7fff...def8以降のメモリの内容が変わったの?
ちょうど32個の値、かつcharの範囲に収まっている
0x7fという値はcharの最大値だから、0x7fを使った部分は、charであることの判定に関連すると推測できる
擬似コードを書き間違えた可能性